using System;
using System.Data;
using ServiceStack.OrmLite.Converters;

namespace ServiceStack.OrmLite;

public static class OrmLiteDialectProviderExtensions
{
    public static string GetParam(this IOrmLiteDialectProvider dialect, string name, string format)
    {
        var ret = dialect.ParamString + (dialect.ParamNameFilter?.Invoke(name) ?? name);
        return format == null
            ? ret
            : string.Format(format, ret);
    }

    public static string GetParam(this IOrmLiteDialectProvider dialect, string name)
    {
        return dialect.ParamString + (dialect.ParamNameFilter?.Invoke(name) ?? name);
    }

    public static string GetParam(this IOrmLiteDialectProvider dialect, int indexNo = 0)
    {
        return dialect.ParamString + indexNo;
    }

    public static string ToFieldName(this IOrmLiteDialectProvider dialect, string paramName)
    {
        return paramName.Substring(dialect.ParamString.Length);
    }

    public static string FmtTable(this string tableName, IOrmLiteDialectProvider dialect = null)
    {
        return (dialect ?? OrmLiteConfig.DialectProvider).NamingStrategy.GetTableName(tableName);
    }

    public static string FmtColumn(this string columnName, IOrmLiteDialectProvider dialect=null)
    {
        return (dialect ?? OrmLiteConfig.DialectProvider).NamingStrategy.GetColumnName(columnName);
    }

    public static string GetQuotedTableName(this IOrmLiteDialectProvider dialect, string tableName) => tableName == null ? null : 
        dialect.QuoteTable(new(tableName));

    public static string GetTableName(this IOrmLiteDialectProvider dialect, string tableName) => tableName == null ? null : 
        dialect.UnquotedTable(new(tableName));

    public static string GetTableName(this IOrmLiteDialectProvider dialect, Type table) => table == null ? null : 
        dialect.UnquotedTable(new(table.GetModelDefinition()));

    public static string GetTableName(this IOrmLiteDialectProvider dialect, ModelDefinition modelDef) => modelDef == null ? null : 
        dialect.UnquotedTable(new(modelDef));

    public static string GetQuotedColumnName(this IOrmLiteDialectProvider dialect,
        ModelDefinition tableDef, FieldDefinition fieldDef)
    {
        return dialect.GetQuotedTableName(tableDef) +
               "." +
               dialect.GetQuotedColumnName(fieldDef);
    }

    public static string GetQuotedColumnName(this IOrmLiteDialectProvider dialect,
        ModelDefinition tableDef, string tableAlias, FieldDefinition fieldDef)
    {
        if (tableAlias == null)
            return dialect.GetQuotedColumnName(tableDef, fieldDef);
            
        return dialect.QuoteTable(tableAlias) //aliases shouldn't have schemas
               + "." +
               dialect.GetQuotedColumnName(fieldDef);
    }

    public static string GetQuotedColumnName(this IOrmLiteDialectProvider dialect,
        ModelDefinition tableDef, string fieldName)
    {
        return dialect.GetQuotedTableName(tableDef) +
               "." +
               dialect.GetQuotedColumnName(fieldName);
    }

    public static string GetQuotedColumnName(this IOrmLiteDialectProvider dialect,
        ModelDefinition tableDef, string tableAlias, string fieldName)
    {
        if (tableAlias == null)
            return dialect.GetQuotedColumnName(tableDef, fieldName);
            
        return dialect.QuoteTable(tableAlias) //aliases shouldn't have schemas 
               + "." +
               dialect.GetQuotedColumnName(fieldName);
    }

    public static object FromDbValue(this IOrmLiteDialectProvider dialect, 
        IDataReader reader, int columnIndex, Type type)
    {
        return dialect.FromDbValue(dialect.GetValue(reader, columnIndex, type), type);
    }

    public static IOrmLiteConverter GetConverter<T>(this IOrmLiteDialectProvider dialect)
    {
        return dialect.GetConverter(typeof(T));
    }

    public static bool HasConverter(this IOrmLiteDialectProvider dialect, Type type)
    {
        return dialect.GetConverter(type) != null;
    }

    public static StringConverter GetStringConverter(this IOrmLiteDialectProvider dialect)
    {
        return (StringConverter)dialect.GetConverter(typeof(string));
    }

    public static DecimalConverter GetDecimalConverter(this IOrmLiteDialectProvider dialect)
    {
        return (DecimalConverter)dialect.GetConverter(typeof(decimal));
    }

    public static DateTimeConverter GetDateTimeConverter(this IOrmLiteDialectProvider dialect)
    {
        return (DateTimeConverter)dialect.GetConverter(typeof(DateTime));
    }

    public static bool IsMySqlConnector(this IOrmLiteDialectProvider dialect) => 
        dialect.GetType().Name == "MySqlConnectorDialectProvider";

    public static void InitDbParam(this IOrmLiteDialectProvider dialect, IDbDataParameter dbParam, Type columnType)
    {
        var converter = dialect.GetConverterBestMatch(columnType);
        converter.InitDbParam(dbParam, columnType);
    }

    public static void InitDbParam(this IOrmLiteDialectProvider dialect, IDbDataParameter dbParam, Type columnType, object value)
    {
        var converter = dialect.GetConverterBestMatch(columnType);
        converter.InitDbParam(dbParam, columnType);
        dbParam.Value = converter.ToDbValue(columnType, value);
    }

    public static string SqlSpread<T>(this IOrmLiteDialectProvider dialect, params T[] values) =>
        OrmLiteUtils.SqlJoin(values, dialect);

    public static string ToAddColumnStatement(this IOrmLiteDialectProvider dialect, Type modelType, FieldDefinition fieldDef) =>
        X.Map(modelType.GetModelDefinition(), x => dialect.ToAddColumnStatement(new(x), fieldDef));

    public static string ToAlterColumnStatement(this IOrmLiteDialectProvider dialect, Type modelType, FieldDefinition fieldDef) =>
        X.Map(modelType.GetModelDefinition(), x => dialect.ToAlterColumnStatement(new(x), fieldDef));

    public static string ToChangeColumnNameStatement(this IOrmLiteDialectProvider dialect, Type modelType, FieldDefinition fieldDef, string oldColumnName) =>
        X.Map(modelType.GetModelDefinition(), x => dialect.ToChangeColumnNameStatement(new (x), fieldDef, oldColumnName));

    public static string ToRenameColumnStatement(this IOrmLiteDialectProvider dialect, Type modelType, string oldColumnName, string newColumnName) => 
        X.Map(modelType.GetModelDefinition(), x => dialect.ToRenameColumnStatement(new(x), oldColumnName, newColumnName));
    public static string ToDropColumnStatement(this IOrmLiteDialectProvider dialect, Type modelType, string columnName) =>
        X.Map(modelType.GetModelDefinition(), x => dialect.ToDropColumnStatement(new(x), columnName));
    public static string ToDropConstraintStatement(this IOrmLiteDialectProvider dialect, Type modelType, string constraintName) =>
        X.Map(modelType.GetModelDefinition(), x => dialect.ToDropConstraintStatement(new(x), constraintName));
}